package com.mrd.util;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class FtpUtil {
    //    本地字符编码
    private static String LOCAL_CHARSET = System.getProperty("file.encoding");
    // FTP协议里面，规定文件名编码为iso-8859-1
    private static String SERVER_CHARSET = "ISO-8859-1";


    private FTPClient ftpClient;

    /**
     * 连接ftp服务器
     */
    public void connect(String url, int port, String username, String password, String remotePath) throws IOException {
        ftpClient = new FTPClient();
        if (!ftpClient.isConnected()) {
            ftpClient.connect(url, port);
        }
        boolean isLogin = ftpClient.login(username, password);
        int reply = ftpClient.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            ftpClient.disconnect();
        }
        // 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码
        if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
            LOCAL_CHARSET = "UTF-8";
        }
        ftpClient.setControlEncoding(LOCAL_CHARSET);
        if (isLogin && remotePath != null && !"".equals(remotePath)) {
            ftpClient.changeWorkingDirectory(new String(remotePath.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
        }
        ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
        //被动模式
        ftpClient.enterLocalPassiveMode();
    }

    /**
     * 跳转到目录
     */
    public void changeWorkingDirectory(String remotePath) throws IOException {
        ftpClient.changeWorkingDirectory(new String(remotePath.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
    }

    /**
     * 关闭FTP连接
     */
    public void closeServer() {
        if (ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param filename 上传到FTP服务器上的文件名
     * @param input    输入流
     * @return 成功返回true，否则返回false
     */
    public boolean uploadFile(String filename, InputStream input) throws IOException {
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        boolean isSuccessful = ftpClient.storeFile(new String(filename.getBytes(LOCAL_CHARSET), SERVER_CHARSET), input);
        input.close();
        return isSuccessful;
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param filename       上传到FTP服务器上的文件名
     * @param uploadFilePath 需要上传的文件路径
     * @return 成功返回true，否则返回false
     */
    public boolean uploadFile(String filename, String uploadFilePath) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(new File(uploadFilePath));
        try {
            return uploadFile(filename, fileInputStream);
        } finally {
            fileInputStream.close();
        }
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param url      FTP服务器hostname
     * @param port     FTP服务器端口
     * @param username FTP登录账号
     * @param password FTP登录密码
     * @param path     FTP服务器保存目录
     * @param filename 上传到FTP服务器上的文件名
     * @param input    输入流
     * @return 成功返回true，否则返回false
     */
    public boolean uploadFile(String url, int port, String username, String password, String path, String filename, InputStream input) throws IOException {
        try {
            this.connect(url, port, username, password, path);
            return uploadFile(filename, input);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            closeServer();
            input.close();
        }
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param url            FTP服务器hostname
     * @param port           FTP服务器端口
     * @param username       FTP登录账号
     * @param password       FTP登录密码
     * @param path           FTP服务器保存目录
     * @param uploadFilePath 需要上传的文件路径
     */
    public boolean uploadFile(String url, int port, String username, String password, String path, String uploadFilePath) throws IOException {
        File uploadFile = new File(uploadFilePath);
        try {
            return uploadFile(url, port, username, password, path, uploadFile.getName(), new FileInputStream(uploadFile));
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 扫描指定路径下的文件
     *
     * @param path 需要扫描的文件夹路径
     * @return 文件名称
     */
    public List<String> getFileList(String path) {
        List<String> fileLists = new ArrayList<>();
        // 获得指定目录下所有文件名
        FTPFile[] ftpFiles = null;
        try {
            ftpFiles = ftpClient.listFiles(new String(path.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; ftpFiles != null && i < ftpFiles.length; i++) {
            FTPFile file = ftpFiles[i];
            if (file.isFile()) {
                fileLists.add(file.getName());
            }
        }
        return fileLists;
    }

    /**
     * 扫描指定路径下的文件
     *
     * @param path 需要扫描的文件夹路径
     * @return 文件名称
     */
    public List<String> getFileList(String url, int port, String username, String password, String path) throws IOException {
        List<String> stringList;
        try {
            connect(url, port, username, password, null);
            stringList = getFileList(path);
        } catch (IOException e) {
            throw e;
        } finally {
            closeServer();
        }
        return stringList;
    }

    /**
     * 从FTP服务器下载文件
     *
     * @param url        FTP服务器hostname
     * @param port       FTP服务器端口
     * @param username   FTP登录账号
     * @param password   FTP登录密码
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名,若为空，则下载当前目录下的所有文件
     * @param localPath  下载后保存到本地的路径
     */
    public boolean downloadFiles(String url, int port, String username, String password, String remotePath, String fileName, String localPath) throws IOException {
        boolean success = false;
        FTPClient ftp = new FTPClient();
        try {
            this.connect(url, port, username, password, remotePath);
            success = this.downloadFiles(fileName, localPath);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            closeServer();
        }
        return success;
    }

    /**
     * 从FTP服务器下载文件
     *
     * @param fileName  要下载的文件名,若为空，则下载当前目录下的所有文件
     * @param localPath 下载后保存到本地的路径
     */
    public boolean downloadFiles(String fileName, String localPath) {
        boolean success = false;
        OutputStream outputStream = null;
        File local = new File(localPath);
        if (!local.exists()) {
            local.mkdirs();
        }
        try {
            if (fileName == null || "".equals(fileName)) {
                FTPFile[] files = ftpClient.listFiles();
                for (FTPFile file : files) {
                    if (file.isFile()) {
                        String fName = file.getName();
                        File targetFile = new File(localPath + "/" + fName);
                        if (!targetFile.exists()) {
                            targetFile.createNewFile();
                        }
                        outputStream = new FileOutputStream(targetFile);
                        ftpClient.retrieveFile(new String(file.getName().getBytes(LOCAL_CHARSET), SERVER_CHARSET), outputStream);
                        outputStream.flush();
                    }
                }
                success = true;
            } else {
                File targetFile = new File(localPath + "/" + fileName);
                if (!targetFile.exists()) {
                    targetFile.createNewFile();
                }
                outputStream = new FileOutputStream(targetFile);
                success = ftpClient.retrieveFile(new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET), outputStream);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                outputStream = null;
            }
        }
        return success;
    }

    /**
     * 从FTP服务器下载文件
     *
     * @param path      要跳转的目录
     * @param fileName  要下载的文件名,若为空，则下载当前目录下的所有文件
     * @param localPath 下载后保存到本地的路径
     */
    public boolean downloadFiles(String path, String fileName, String localPath) throws IOException {
        ftpClient.changeWorkingDirectory(path);
        return downloadFiles(fileName, localPath);
    }

    /**
     * 删除FTP服务器文件
     *
     * @param url        FTP服务器hostname
     * @param port       FTP服务器端口
     * @param username   FTP登录账号
     * @param password   FTP登录密码
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名
     */
    public boolean delFile(String url, int port, String username, String password, String remotePath, String fileName) throws IOException {
        boolean success;
        try {
            this.connect(url, port, username, password, remotePath);
            success = this.delFile(fileName);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            closeServer();
        }
        return success;
    }

    /**
     * 删除FTP服务器下载文件
     *
     * @param fileName 要下载的文件名
     */
    public boolean delFile(String fileName) throws IOException {
        return ftpClient.deleteFile(new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
    }

    /**
     * 下载后删除文件
     *
     * @param remotePath 要下载文件的目录(已在当前目录则可为null)
     * @param fileName   要下载的文件，为空则下载并删除全部
     * @param localPath  本地存放路径
     */
    public void downloadThenDel(String remotePath, String fileName, String localPath) throws IOException {
        List<String> fileList = null;
        if (fileName == null || "".equals(fileName)) {
            fileList = this.getFileList(remotePath);
        }
        if (remotePath != null) {
            ftpClient.changeWorkingDirectory(remotePath);
        }
        if (fileName != null && !"".equals(fileName)) {
            if (this.downloadFiles(fileName, localPath)) {
                this.delFile(fileName);
            }
        } else {
            for (String file : fileList) {
                if (downloadFiles(file, localPath)) {
                    delFile(file);
                }
            }
        }
    }

    /**
     * 重命名文件
     */
    public boolean rename(String from, String to) throws IOException {
        return ftpClient.rename(new String(from.getBytes(LOCAL_CHARSET), SERVER_CHARSET), new String(to.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
    }
}
